package com.roger.homecenterscreen.voice

import android.content.Context
import android.util.Log
import com.roger.homecenterscreen.config.BusinessConfig
import info.mqtt.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.IMqttActionListener
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended
import org.eclipse.paho.client.mqttv3.MqttConnectOptions
import org.eclipse.paho.client.mqttv3.MqttMessage
import org.json.JSONObject
import java.util.UUID

data class OutboundMessage(
    val query: String,
    val user_id: Any?,
    val demp_id: String,
    val from_terminal: String? = "terminal",
    val type: String = "0",
    val mode: String = "normal",
    val plan_id: String? = null,
    val device_type: String? = null,
    val session_id: String? = null,
    val message_id: String? = null,
    val conversation_id: String? = null,
    val device_id: String? = null,
    val reply: Boolean? = false,
    val sense: String? = null,
    val mediaList: Any? = null,
    val datasource_id: String? = null
)

data class InboundMessage(
    val content: String,
    val timestamp: String?,
    val type: String,
    val message_id: String?,
    val index: Int,
    val user_id: Any?,
    val is_end: Boolean,
    val is_stream: Boolean,
    val plan_id: String?,
    val session_id: String?,
    val demp_id: String?,
    val response_mode: String?,
    val device_type: String?,
    val finish_reason: String?,
    val tool_name: String?,
    val device_id: String?,
    val deviceMessageType: Int,
    val deviceMessageTo: String?
)

class MqttServiceManager(
    private val context: Context,
    private val onInbound: (InboundMessage) -> Unit
) {
    private val TAG = "MqttServiceManager"
    private var client: MqttAndroidClient? = null
    private var clientId = "hcs-${UUID.randomUUID()}"

    fun connectAndSubscribe(): Boolean {
        try {
            BusinessConfig.load(context)
            val scheme = if (BusinessConfig.mqttUseSsl) "ssl" else "tcp"
            val serverUri = "$scheme://${BusinessConfig.mqttHost}:${BusinessConfig.mqttPort}"
            try {
                client?.disconnect()
            } catch (_: Exception) {}
            try {
                client?.unregisterResources()
            } catch (_: Exception) {}
            try {
                client?.close()
            } catch (_: Exception) {}
            val c = MqttAndroidClient(context.applicationContext, serverUri, clientId)
            client = c

            val opts = MqttConnectOptions().apply {
                isAutomaticReconnect = true
                isCleanSession = true
                userName = BusinessConfig.mqttUsername
                password = (BusinessConfig.mqttPassword ?: "").toCharArray()
            }

            c.setCallback(object : MqttCallbackExtended {
                override fun connectComplete(reconnect: Boolean, serverURI: String?) {
                    Log.i(TAG, "MQTT connected: $serverURI, reconnect=$reconnect")
                    // 订阅服务端下行
                    try { c.subscribe("soul2user", 1) } catch (e: Exception) { Log.e(TAG, "subscribe soul2user", e) }
                }
                override fun messageArrived(topic: String?, message: MqttMessage?) {
                    val payload = message?.payload?.let { String(it) } ?: return
                    try {
                        val j = JSONObject(payload)
                        val inbound = InboundMessage(
                            content = j.optString("content"),
                            timestamp = j.optString("timestamp", null),
                            type = j.optString("type", "0"),
                            message_id = j.optString("message_id", null),
                            index = j.optInt("index", 0),
                            user_id = j.opt("user_id"),
                            is_end = j.optBoolean("is_end", false),
                            is_stream = j.optBoolean("is_stream", true),
                            plan_id = j.optString("plan_id", null),
                            session_id = j.optString("session_id", null),
                            demp_id = j.optString("demp_id", null),
                            response_mode = j.optString("response_mode", "streaming"),
                            device_type = j.optString("device_type", null),
                            finish_reason = j.optString("finish_reason", null),
                            tool_name = j.optString("tool_name", null),
                            device_id = j.optString("device_id", null),
                            deviceMessageType = j.optInt("deviceMessageType", 0),
                            deviceMessageTo = j.optString("deviceMessageTo", null)
                        )
                        onInbound(inbound)
                    } catch (e: Exception) {
                        Log.e(TAG, "MQTT inbound parse error", e)
                    }
                }
                override fun connectionLost(cause: Throwable?) {
                    Log.w(TAG, "MQTT connection lost", cause)
                }
                override fun deliveryComplete(token: IMqttDeliveryToken?) {}
            })

            c.connect(opts, null, object : IMqttActionListener {
                override fun onSuccess(asyncActionToken: org.eclipse.paho.client.mqttv3.IMqttToken?) {
                    Log.i(TAG, "MQTT connect success")
                }
                override fun onFailure(asyncActionToken: org.eclipse.paho.client.mqttv3.IMqttToken?, exception: Throwable?) {
                    Log.e(TAG, "MQTT connect failure", exception)
                }
            })
            return true
        } catch (e: Exception) {
            Log.e(TAG, "MQTT connect error", e)
            return false
        }
    }

    fun publishOutbound(message: OutboundMessage) {
        val c = client ?: return
        try {
            val topic = "call_demp_soul/${BusinessConfig.dempId}"
            val j = JSONObject().apply {
                put("query", message.query)
                put("user_id", message.user_id)
                put("demp_id", message.demp_id)
                put("from_terminal", message.from_terminal)
                put("type", message.type)
                put("mode", message.mode)
                put("plan_id", message.plan_id)
                put("device_type", message.device_type)
                put("session_id", message.session_id)
                put("message_id", message.message_id)
                put("conversation_id", message.conversation_id)
                put("device_id", message.device_id)
                put("reply", message.reply)
                put("sense", message.sense)
                if (message.mediaList != null) put("mediaList", message.mediaList)
                put("datasource_id", message.datasource_id)
            }
            val mqttMsg = MqttMessage(j.toString().toByteArray(Charsets.UTF_8)).apply { qos = 1 }
            c.publish(topic, mqttMsg)
        } catch (e: Exception) {
            Log.e(TAG, "MQTT publish error", e)
        }
    }

    fun disconnect() {
        try { client?.disconnect() } catch (_: Exception) {}
        try { client?.unregisterResources() } catch (_: Exception) {}
        try { client?.close() } catch (_: Exception) {}
        client = null
    }
}
